package com.yzf.enterprise.framework.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;

/**
 * @author wangzhen
 * @title: DistributedLockUtil
 */
@Slf4j
public class DistributedLockUtil {

    static final Long LOCK_SUCCESS = 1L;

    private final static DefaultRedisScript<Long> LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call('pexpire', KEYS[1], ARGV[2]) else return 0 end"
            , Long.class
    );

    private final static DefaultRedisScript<Long> UNLOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return -1 end"
            , Long.class
    );


    /**
     * 尝试获取分布式锁
     *
     * @param redis
     * @param lockKey
     * @param value
     * @param timeout
     * @return
     */
    public static boolean tryGetDistributedLock(RedisTemplate redis, String lockKey, String value, long timeout) {

        Object result = redis.execute(LOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value, timeout);
        return LOCK_SUCCESS.equals(result);
    }


    /**
     * 释放分布式锁
     *
     * @param redis   Redis客户端
     * @param lockKey 锁
     * @param value
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(RedisTemplate redis, String lockKey, String value) {


        Object result = redis.execute(UNLOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value);
        return LOCK_SUCCESS.equals(result);
    }

    /**
     * 加锁
     *
     * @param key   seckillId
     * @param value 当前时间+超时时间
     * @return
     */
    public static boolean lock(StringRedisTemplate redisTemplate, String key, String value) {
        // 可以设置返回true
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (isLock) {
            return true;
        }
        String currentValue = redisTemplate.opsForValue().get(key);
        // 如果锁已经过期
        if (!StringUtils.isEmpty(currentValue) && Long.valueOf(currentValue) < System.currentTimeMillis()) {
            // 获取上一个锁的时间，并设置新锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                log.info("锁过期并返回true");
                return true;
            }
        }
        return false;
    }

    /**
     * 解锁
     *
     * @param key
     * @return
     */
    public static void unlock(StringRedisTemplate redisTemplate, String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            log.error("redis分布式锁，解锁异常, {}", e.getMessage());
        }

    }
}
